#include<hashmap.h>

static int hashmaskcode;
static int hashmapload;
static bool expandflg = false;
static pthread_rwlock_t expandlock;
//扩表用写锁，扩头用读锁
static listhead **buckets;
static pthread_t expthread;

/***
 * void hashmap_init()
 * 初始化哈希表
 * 
 */
void hashmap_init(){
    hashmaskcode = HASHMAP_INIT_SIZE - 1;
    //初始值大小应当是2^n hashmaskcode一直是2^n-1
    buckets = (listhead **)malloc(HASHMAP_INIT_SIZE * sizeof(listhead *));
    auto listheads = (listhead *)malloc(HASHMAP_INIT_SIZE * sizeof(listhead));
    for (int i = 0; i <= HASHMAP_INIT_SIZE;i++){
        buckets[i] = listheads;
        listheads->nxt = nullptr;
        listheads->load = 0;
        listheads->localmask = hashmaskcode;
        listheads->expandflg = false;
        pthread_rwlock_init(&listheads->rwl, NULL);
        listheads++;
    }
    LOG_MSG("hash map init");
}

/***
 * uint32_t hashfunc(char* key,int key_size)
 * BKDRhash
 * 
 */
uint32_t hashfunc(char *key,int key_size){
    uint32_t seed = 131313, hash = 0;
    for (int i = 0; i < key_size;i++){
        hash = hash * seed + key[i];
    }
    return hash;
}

/**
 * listhead *hashmap_gethead(char *key,int keylen,pthread_rwlock_t *rwl)
 * 带锁取得链表头
 * 
 */
listhead* hashmap_gethead(char *key,int keylen,pthread_rwlock_t *&rwl,const bool isread){
    assert(keylen > 0);
    auto hashcode = hashfunc(key, keylen);
    listhead *ret;
    while(1){
        ret = buckets[hashcode & hashmaskcode];
        auto tmplm = ret->localmask;
        rwl = &ret->rwl;
        if(isread)
            assert(pthread_rwlock_rdlock(rwl) == 0);
        else
            assert(pthread_rwlock_wrlock(rwl)==0);
        if(tmplm!=ret->localmask) assert(pthread_rwlock_unlock(rwl) == 0);
        else break;
    }
    return ret;
}

/**
 * void * hashmap_rehash_hashmap(void *)
 * 整个大扩表，开一个线程慢慢搞吧
 * 输入输出都是nullptr
 * 
 */
void *hashmap_rehash_hashmap(void *p){
    pthread_rwlock_wrlock(&expandlock);
    //扩表拿写锁：不会有listhead动
    int nowsize = hashmaskcode + 1;
    auto newbuckets = (listhead **)malloc(sizeof(listhead *) * (nowsize<<1));
    for (int i = 0; i <= hashmaskcode;i++)
        newbuckets[i] = newbuckets[i | nowsize] = buckets[i];
    auto tmp = buckets;
    buckets = newbuckets;
    hashmaskcode = (hashmaskcode << 1 )+ 1;
    free(tmp);
    pthread_rwlock_unlock(&expandlock);
    expandflg = false;
    return nullptr;
}

/**
 * void* hashmap_rehash_listhead(void *lsh)
 * 扩展一个链表头开一个线程慢慢搞
 * 输出nullptr
 */
void *hashmap_rehash_listhead(void*p){
    auto lha=(listhead*)p;
    pthread_rwlock_rdlock(&expandlock);
    //扩链表头用读锁，不会一遍更新旧表，一边复制到新表
    assert(pthread_rwlock_wrlock(&lha->rwl) == 0);
    int tmpcode = lha->nxt->hashcode & lha->localmask;
    int choicebit = lha->localmask + 1;
    auto lhb = (listhead *)mempool_malloc(sizeof(listhead));
    lhb->nxt = nullptr;
    lhb->load = 0;
    lhb->localmask = (lha->localmask<<1)+1;
    lhb->expandflg = true;

    hashnode *pre=nullptr;
    for (auto cur = lha->nxt; cur != nullptr;)
    {
        auto tmp = cur->nxt;
        if (cur->hashcode & choicebit){
            lha->load--;
            if(pre==nullptr)
                lha->nxt = cur->nxt;
            else
                pre->nxt = cur->nxt;
            lhb->load++;
            cur->nxt = lhb->nxt;
            lhb->nxt = cur;
        }
        pre = cur;
        cur = tmp;
    }

    //lha->localmask choicebit
    //hashmaskcode
    int exph = hashmaskcode^((choicebit<<1)+1);
    int ywcnt = 0;
    while (!(exph & 1)) exph >>= 1,ywcnt++;
    for (int i = 0; i <= exph;i++){
        buckets[i << ywcnt | choicebit | tmpcode] = lhb;
        buckets[i << ywcnt | tmpcode] = lha;
    }
    //复制到新表

    lha->localmask = lha->localmask << 1 + 1;
    assert(pthread_rwlock_unlock(&lha->rwl) == 0);
    pthread_rwlock_unlock(&expandlock);
    //在扩容的时候 改变值 然后释放锁 其他线程获得锁检查一致性
    return nullptr;
}

/**
 * hashnode* hashmap_makenode(char*,int,char*,int)
 * 新建一个hashnode 使用mempool申请地址 
 * 返回指针，使用mempoll_free(指针)就可以回收资源
 */
hashnode* hashmap_makenode(char*key,int keylen,char*val,int vallen){
    auto p = (uint8_t *)mempool_malloc(sizeof(hashnode) + keylen + vallen);
    auto ret = (hashnode *)p;
    ret->key = (char *)(p + sizeof(key));
    ret->val = ret->key + keylen;
    ret->keylen = keylen;
    ret->vallen = vallen;
    ret->hashcode = hashfunc(key, keylen);
    for (int i = 0; i < keylen;i++)
        ret->key[i]=key[i];
    for (int i = 0; i < vallen;i++)
        ret->val[i] = val[i];
    return ret;
}

/**
 * int hashmap_get(char*,int ,char*)
 * 获得指定key的value 返回value的长度
 * 
 */
int hashmap_get(char* key,int keylen,char* val){
    pthread_rwlock_t *rwl;
    listhead *lhp = hashmap_gethead(key, keylen, rwl, true);
    hashnode *cur = lhp->nxt;
    hashnode tmp(key,keylen);
    while (cur != nullptr){
        if(tmp==*cur) break;
        cur = cur->nxt;
    }
    if(cur==nullptr){
        assert(pthread_rwlock_unlock(rwl)==0);
        return 0;
    }
    for (int i = 0; i < cur->keylen;i++) val[i] = cur->val[i];
    assert(pthread_rwlock_unlock(rwl)==0);
    return cur->vallen;
}

/**
 * int hashmap_del(char*,int);
 * 删除指定的键值对 没有返回-1
 * 
 */
int hashmap_del(char*key,int keylen){
    pthread_rwlock_t *rwl;
    listhead *lhp = hashmap_gethead(key, keylen, rwl, false);
    hashnode *cur = lhp->nxt,*pre=nullptr;
    hashnode tmp(key, keylen);
    while(cur!=nullptr){
        if(tmp==*cur)
            break;
        pre = cur;
        cur = cur->nxt;
    }
    if (cur == nullptr){
        assert(pthread_rwlock_unlock(rwl) == 0);
        return -1;
    }
    lhp->load--;
    _FAA(&hashmapload, -1);
    if (pre == nullptr)
        lhp->nxt = cur->nxt;
    else
        pre->nxt = cur->nxt;
    mempool_free(cur);
    assert(pthread_rwlock_unlock(rwl) == 0);
    return 0;
}

/**
 * int hashmap_set(char* int char* int)
 * 找不到就添加 找得到就删改
 * 如果因数过大，直接新建线程扩表
 * 
 */
int hashmap_set(char* key,int keylen,char *val,int vallen){
    pthread_rwlock_t *rwl;
    listhead *lhp = hashmap_gethead(key, keylen, rwl, false);
    hashnode *cur = lhp->nxt,*pre=nullptr;
    hashnode tmp(key, keylen);
    while(cur!=nullptr){
        if(tmp==*cur)
            break;
        pre = cur;
        cur = cur->nxt;
    }
    if(cur==nullptr){
        //没有找到 插入(从头插，方便下次访问)
        auto newnode = hashmap_makenode(key, keylen, val, vallen);
        newnode->nxt = lhp->nxt;
        lhp->nxt = newnode;
        lhp->load++;
        _FAA(&hashmapload, 1);
    }
    else{
        //找到了
        if(cur->vallen>=vallen){
            for (int i = 0; i < vallen;i++)
                cur->val[i] = val[i];
            cur->vallen = vallen;
        }
        else{
            //需要来个新的
            auto newnode = hashmap_makenode(key, keylen, val, vallen);
            auto tmpp = lhp->nxt;
            newnode->nxt = tmpp->nxt;
            lhp->nxt = newnode;
            mempool_free(tmpp);
        }
    }

    if(lhp->load<=HASHMAP_LOAD_MAX){
        assert(pthread_rwlock_unlock(rwl) == 0);
        return 0;
    }
    //扩表

    if(hashmapload>hashmaskcode/HASHMAP_LOAD_MAX&&lhp->localmask<hashmaskcode){
        if(_CAS(&expandflg,false,true))
            assert(pthread_create(&expthread, NULL, hashmap_rehash_hashmap, nullptr)==0);
    }

    if(_CAS(&lhp->expandflg,false,true))
        assert(pthread_create(&lhp->expthread, NULL, hashmap_rehash_listhead, nullptr) == 0);
    //扩表： 发送信号阻断标志位 expandflg
    assert(pthread_rwlock_unlock(rwl) == 0);
    return 0;
}
